<?php
// +----------------------------------------------------------------------
// | KITEGO-Admin「开箱即用」「人人全栈」
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2024 https://www.kitego.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed KITEGO并不是自由软件，未经许可不能去掉KITEGO相关版权
// +----------------------------------------------------------------------
// | Author: KITEGO Team <bd@kitego.cn>
// +----------------------------------------------------------------------

namespace kitego\services\common;

class ApiSecurityService
{
    // 请求参数加密密钥
    private $encryptKey;

    // 请求签名密钥
    private $signKey;

    // 请求超时时间（秒）
    private $timeout = 60;

    public function __construct()
    {
        // 从配置文件获取密钥
        $this->encryptKey = 'sdfasdfasdfasdf';
        $this->signKey = 'asdfasdfasdf';

        // 检查密钥是否存在
        if (empty($this->encryptKey) || empty($this->signKey)) exception('API安全密钥未配置');
    }

    /**
     * 设置超时时间
     */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;

        return $this;
    }

    /**
     * 生成请求签名
     */
    public function generateSign($params)
    {
        // 过滤空值和签名参数
        $params = array_filter($params, function ($value) {
            return $value !== '' && $value !== null;
        });

        // 移除可能存在的签名参数
        if (isset($params['sign'])) {
            unset($params['sign']);
        }

        // 排除s参数
        if (isset($params['s'])) {
            unset($params['s']);
        }

        // 按照键名排序
        ksort($params);

        // 拼接参数
        $stringToSign = '';
        foreach ($params as $key => $value) {
            $stringToSign .= "{$key}={$value}&";
        }
        $stringToSign = rtrim($stringToSign, '&');

        // 生成HMAC-SHA256签名
        return hash_hmac('sha256', $stringToSign, $this->signKey);
    }

    /**
     * 验证签名
     */
    public function verifySign($params)
    {
        // 检查是否包含签名
        if (!isset($params['sign'])) exception('sign fail');

        // 检查是否包含时间戳
        if (!isset($params['timestamp'])) exception('timestamp fail');

        // 检查时间戳有效性（防止重放攻击）
        $timestamp = $params['timestamp'];
        if (time() - $timestamp > $this->timeout) exception('timestamp expire');

        // 检查是否是重复请求
        $nonceKey = 'api_nonce_' . $params['nonce'];
        if (CacheService::get($nonceKey)) exception('nonce expire');

        // 验证签名
        $sign = $params['sign'];
        $calculatedSign = $this->generateSign($params);

        // 存储随机数，防止重放攻击
        CacheService::set($nonceKey, 1, $this->timeout);

        if (!hash_equals($sign, $calculatedSign)) exception('签名验证失败');
    }

    /**
     * 加密数据
     */
    public function encrypt($data)
    {
        // 将数组转换为JSON
        if (is_array($data)) {
            $data = json_encode($data, JSON_UNESCAPED_UNICODE);
        }

        // 使用AES-256-CBC加密
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));
        $encrypted = openssl_encrypt($data, 'AES-256-CBC', $this->encryptKey, 0, $iv);

        // 返回Base64编码的IV和密文
        return base64_encode($iv . '::' . $encrypted);
    }

    /**
     * 解密数据
     */
    public function decrypt($encryptedData)
    {
        // 解码Base64
        $encryptedData = base64_decode($encryptedData);

        // 分离IV和密文
        list($iv, $encrypted) = explode('::', $encryptedData, 2);

        // 解密
        $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $this->encryptKey, 0, $iv);

        // 尝试解析JSON
        $data = json_decode($decrypted, true);

        return json_last_error() === JSON_ERROR_NONE ? $data : $decrypted;
    }

    /**
     * 生成API请求参数（包含签名和时间戳）
     */
    public function handleEncryptRequest($data = [])
    {
        $params = [
            'timestamp' => time(),
            'nonce' => uniqid(),
            'data' => $this->encrypt($data)
        ];

        $params['sign'] = $this->generateSign($params);

        return $params;
    }

    /**
     * 返回加密响应
     */
    public function handleDecryptRequest($params = [])
    {
        // 验证签名
        $this->verifySign($params);

        // 解密数据
        return $this->decrypt($params['data']);
    }

    /**
     * 处理API响应
     */
    public function processResponse($response)
    {
        $content = $response->getContent();

        try {
            $data = json_decode($content, true);

            if (json_last_error() === JSON_ERROR_NONE) {
                // 如果响应是JSON格式，检查是否有加密数据
                if (isset($data['data'])) {
                    $data['data'] = $this->decrypt($data['data']);
                }

                return $data;
            }

            // 如果不是JSON，尝试直接解密
            return $this->decrypt($content);

        } catch (\Exception $e) {
            return fail('响应解析失败:' . $e->getMessage());
        }
    }
}